// Tierless
// Copyright © 2010 Alphora
// This file is licensed under a modified BSD-license (see tierless_license.txt).
using System;
using System.Runtime.Serialization;

using Tierless.Framework.Entities;

namespace Tierless.Framework.EntityTypes
{
	/// <summary>
	/// Represents the definition of a column within an entity type.
	/// </summary>
	[DataContract]
	public class EntityTypeColumn : NamedEntityTypeObject
	{
		/// <summary>
		/// Initializes a new instance of the EntityTypeColumn class.
		/// </summary>
		/// <param name="name">The name of the column.</param>
		public EntityTypeColumn(string name)
			: base(name)
		{
			Title = name;
		}

		/// <summary>
		/// Initializes a new instance of the EntityTypeColumn class.
		/// </summary>
		/// <param name="name">The name of the column.</param>
		/// <param name="entityColumnTypeName">The name of the type that will support value representation for this column.</param>
		/// <param name="nativeTypeName">The native type of values for this column.</param>
		public EntityTypeColumn(string name, string entityColumnTypeName, string nativeTypeName)
			: this(name)
		{
			EntityColumnTypeName = entityColumnTypeName;
			NativeTypeName = nativeTypeName;
		}

		/// <summary>
		/// Initializes a new instance of the EntityTypeColumn class.
		/// </summary>
		/// <param name="name">The name of the column.</param>
		/// <param name="entityColumnTypeName">The name of the type that will support value representation for this column.</param>
		/// <param name="nativeTypeName">The native type of values for this column.</param>
		/// <param name="maxLength">The maximum length of values for this column.</param>
		public EntityTypeColumn(string name, string entityColumnTypeName, string nativeTypeName, int maxLength)
			: this(name, entityColumnTypeName, nativeTypeName)
		{
			MaxLength = maxLength;
		}

		/// <summary>
		/// Gets or sets the title of the column.
		/// </summary>
		[DataMember]
		public string Title { get; set; }

		/// <summary>
		/// Gets or sets the hint associated with the column.
		/// </summary>
		[DataMember]
		public string Hint { get; set; }

		/// <summary>
		/// Gets or sets whether or not the column is required.
		/// </summary>
		[DataMember]
		public bool IsRequired { get; set; }

		/// <summary>
		/// Gets or sets whether or not the column is temporal.
		/// </summary>
		[DataMember]
		public bool IsTemporal { get; set; }

		/// <summary>
		/// Gets or sets the kind of column (stored or computed).
		/// </summary>
		[DataMember]
		public EntityColumnType ColumnType { get; set; }

		/// <summary>
		/// Gets whether or not the column is stored.
		/// </summary>
		public bool IsStored
		{
			get { return ColumnType == EntityColumnType.Stored; }
		}

		/// <summary>
		/// Gets whether or not the column is computed.
		/// </summary>
		public bool IsComputed
		{
			get { return ColumnType == EntityColumnType.Computed; }
		}

		/// <summary>
		/// Gets whether or not the column represents a key for the entity type.
		/// </summary>
		[DataMember]
		public bool IsKey { get; set; }

		/// <summary>
		/// Gets or sets the type of the column class used to provide value representation for the column.
		/// </summary>
		[DataMember]
		public string EntityColumnTypeName { get; set; }

		/// <summary>
		/// Gets or sets the type of values for the column.
		/// </summary>
		[DataMember]
		public string NativeTypeName { get; set; }

		/// <summary>
		/// Gets or sets the name of the binding property for this column.
		/// </summary>
		[DataMember]
		public string BindingPropertyName { get; set; }

		/// <summary>
		/// Gets or sets the maximum length for the column.
		/// </summary>
		[DataMember]
		public int MaxLength { get; set; }

		/// <summary>
		/// Gets or sets the precision for the column.
		/// </summary>
		[DataMember]
		public int Precision { get; set; }

		/// <summary>
		/// Gets or sets the scale for the column.
		/// </summary>
		[DataMember]
		public int Scale { get; set; }

		/// <summary>
		/// Gets or sets whether or not values in the column support comparison.
		/// </summary>
		[DataMember]
		public bool IsComparable { get; set; }

		protected internal virtual EntityColumn CreateColumn(Entity entity, int index)
		{
			return (EntityColumn)Activator.CreateInstance(Type.GetType(EntityColumnTypeName, true, true), new object[] { entity, this, index });
		}

		private object _defaultValue;

		/// <summary>
		/// Gets the default value for the column, if any.
		/// </summary>
		[DataMember]
		public object DefaultValue
		{
			get { return _defaultValue; }
			set { _defaultValue = value; }
		}

		private EntityTypeColumnDefault _columnDefault;

		/// <summary>
		/// Gets or sets the default definition for the column, if any.
		/// </summary>
		[DataMember]
		public EntityTypeColumnDefault Default
		{
			get { return _columnDefault; }
			set { _columnDefault = value; }
		}

		protected internal virtual object DefaultData(EntityType entityType)
		{
			return _columnDefault != null ? _columnDefault.GetDefaultValue() : _defaultValue;
		}

		/// <summary>
		/// Fired whenever a column is changing.
		/// </summary>
		public event EntityColumnChangeEventHandler ColumnChanging;

		protected void DoColumnChanging(Entity entity, EntityColumn column, object currentValue, object newValue)
		{
			if (ColumnChanging != null)
				ColumnChanging(entity, column, currentValue, newValue);
		}

		protected internal virtual void OnColumnChanging(Entity entity, EntityColumn column, object currentValue, object newValue)
		{
			DoColumnChanging(entity, column, currentValue, newValue);

			// Override to act on a changing column value
		}

		/// <summary>
		/// Fired whenever a column has changed.
		/// </summary>
		public event EntityColumnChangeEventHandler ColumnChanged;

		protected void DoColumnChanged(Entity entity, EntityColumn column, object oldValue, object newValue)
		{
			if (ColumnChanged != null)
				ColumnChanged(entity, column, oldValue, newValue);
		}

		protected internal virtual void OnColumnChanged(Entity entity, EntityColumn column, object oldValue, object newValue)
		{
			DoColumnChanged(entity, column, oldValue, newValue);

			// Override to act on a changed column value
		}

		protected internal virtual object GetComputedValue(Entity entity, object currentValue)
		{
			return currentValue;
		}

		protected internal virtual void SetComputedValue(Entity entity, object newValue)
		{}

		protected internal virtual bool DetermineIsRequired(Entity entity, bool isRequired)
		{
			return isRequired;
		}

		protected internal virtual bool DetermineIsReadOnly(Entity entity, bool isReadOnly)
		{
			return isReadOnly;
		}
	}
}